package com.example.academic_affairs_system.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.academic_affairs_system.common.RPage;
import com.example.academic_affairs_system.exception.SystemException;
import com.example.academic_affairs_system.mapper.AasClassMapper;
import com.example.academic_affairs_system.model.bo.AasClassUserBO;
import com.example.academic_affairs_system.model.bo.TimetableBO;
import com.example.academic_affairs_system.model.dto.classCourses.TimeTableCoursesDTO;
import com.example.academic_affairs_system.model.dto.classCourses.TimeTableDTO;
import com.example.academic_affairs_system.model.dto.classStudent.ClassStudentPageDTO;
import com.example.academic_affairs_system.model.dto.clazz.HandlerClassDTO;
import com.example.academic_affairs_system.model.entity.AasClass;
import com.example.academic_affairs_system.model.entity.AasClassStudent;
import com.example.academic_affairs_system.model.vo.LoginResponseVO;
import com.example.academic_affairs_system.model.vo.classStudent.ClassStudentVO;
import com.example.academic_affairs_system.model.vo.clazz.ClassInfoVO;
import com.example.academic_affairs_system.model.vo.clazz.ClassStaticsVO;
import com.example.academic_affairs_system.service.IAasClassService;
import com.example.academic_affairs_system.service.IAasClassStudentService;
import com.example.academic_affairs_system.utils.TokenUtils;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 服务实现类
 *
 * @author hjx
 * @since 2023-01-31
 */
@Service
public class AasClassServiceImpl extends ServiceImpl<AasClassMapper, AasClass>
    implements IAasClassService {
  @Resource IAasClassStudentService iAasClassStudentService;
  @Resource HttpServletRequest request;

  @Override
  public List<AasClass> getClassByDirectorsId(long userId) {
    return baseMapper.selectList(
        Wrappers.<AasClass>lambdaQuery().eq(AasClass::getDirectorsUserId, userId));
  }

  @Override
  public List<AasClass> getClassByCurrentUser() {
    LoginResponseVO loginResponseVO = TokenUtils.parseToken(request);
    List<AasClass> classInfos = getClassByDirectorsId(loginResponseVO.getUserId());
    if (CollectionUtil.isEmpty(classInfos)) {
      // 他不是班级负责人，就往下查询是否为学生
      classInfos = Collections.singletonList(getClassByStudentId(loginResponseVO.getUserId()));
    }
    return classInfos;
  }

  @Override
  public AasClass getClassByStudentId(long userId) {
    return baseMapper.selectById(iAasClassStudentService.getClassIdByStudentId(userId));
  }

  @Override
  public AasClassUserBO getClassUserByUserId(long userId) {
    // 先判断是否为老师
    List<AasClass> aasClassList = getClassByDirectorsId(userId);
    String classId, classname, classDesc, classType;
    AasClass aasClass;
    String position, positionDesc;
    if (aasClassList != null && !aasClassList.isEmpty()) {
      StringJoiner idBuilder = new StringJoiner(","),
          classnameBuilder = new StringJoiner(","),
          descBuilder = new StringJoiner(","),
          typeBuilder = new StringJoiner(",");
      for (AasClass aClass : aasClassList) {
        idBuilder.add(String.valueOf(aClass.getId()));
        classnameBuilder.add(aClass.getName());
        descBuilder.add(aClass.getDesc());
        typeBuilder.add(aClass.getType());
      }
      classId = idBuilder.toString();
      classname = classnameBuilder.toString();
      classDesc = descBuilder.toString();
      classType = typeBuilder.toString();
      position = "班级负责人";
      positionDesc = "负责班级的一切事务";
    } else {
      // 指定的 user id 不是负责人
      AasClassStudent classStudentByStudentId =
          iAasClassStudentService.getClassStudentByStudentId(userId);
      if (classStudentByStudentId == null) {
        return null;
      }
      aasClass = baseMapper.selectById(classStudentByStudentId.getClassId());
      classId = String.valueOf(aasClass.getId());
      classname = aasClass.getName();
      classDesc = aasClass.getDesc();
      classType = aasClass.getType();
      position = classStudentByStudentId.getPosition();
      positionDesc = classStudentByStudentId.getPositionDesc();
    }

    return AasClassUserBO.builder()
        .userId(userId)
        .classId(classId)
        .classname(classname)
        .classdesc(classDesc)
        .classType(classType)
        .position(position)
        .positionDesc(positionDesc)
        .build();
  }

  @Override
  public ClassInfoVO getClassInfoByClassId(long classId) {
    LoginResponseVO loginResponseVO = TokenUtils.parseToken(request);
    AasClass aasClass = baseMapper.selectById(classId);
    RPage<ClassStudentVO> rPage =
        iAasClassStudentService.getClassStudentVOById(
            ClassStudentPageDTO.builder().id(classId).page(0).limit(200).build());
    List<ClassStudentVO> classStudents = rPage.getData();
    long boy = 0, girl = 0;
    for (ClassStudentVO classStudent : classStudents) {
      if (classStudent.getSex().equals("男")) {
        boy++;
      } else {
        girl++;
      }
    }
    return ClassInfoVO.builder()
        .isDirectors(aasClass.getDirectorsUserId().equals(loginResponseVO.getUserId()))
        .classCode(aasClass.getClassCode())
        .desc(aasClass.getDesc())
        .name(aasClass.getName())
        .type(aasClass.getType())
        .boyNum(boy)
        .girlNum(girl)
        .timetable(aasClass.getTimetable())
        .build();
  }

  @Override
  public List<ClassStaticsVO> getClassStaticsByDirectorsId() {
    // 根据班级的 code 进行分组
    /*    Map<Long, List<ClassInfoVO>> reMap =
    data.stream()
        .collect(
            Collectors.toMap(
                ClassInfoVO::getClassCode,
                Lists::newArrayList,
                (List<ClassInfoVO> saveList, List<ClassInfoVO> reList) -> {
                  reList.addAll(saveList);
                  return reList;
                }));*/
    return baseMapper.getClassStudentDirectorStatics(TokenUtils.parseToken(request).getUserId());
  }

  @Override
  public long createClass(HandlerClassDTO dto) {
    AasClass aasClass = dto.buildCreateClass();
    try {
      baseMapper.insert(aasClass);
    } catch (DuplicateKeyException e) {
      throw new SystemException(-999, "已有重复信息的班级", "请确保班级名称、代码编号没有被占用");
    }
    return aasClass.getId();
  }

  @Override
  public int edit(HandlerClassDTO dto) {
    return baseMapper.updateById(dto.buildCreateClass());
  }

  @Override
  public int arrangeClassTimetable(TimeTableDTO dto) {
    Long classId = dto.getClassId();
    AasClass aasClass = getById(classId);
    if (aasClass == null) {
      throw new SystemException(-999, "班级不存在", null);
    }
    String timetable = aasClass.getTimetable();
    // 进行数据处理，新数据覆盖旧数据
    TimetableBO oldTimetable = JSONUtil.toBean(timetable, TimetableBO.class);
    TimetableBO newTimetable = dto.buildBO();
    TimetableBO build =
        TimetableBO.builder()
            .monday(duplication(newTimetable.getMonday(), oldTimetable.getMonday()))
            .tuesday(duplication(newTimetable.getTuesday(), oldTimetable.getTuesday()))
            .wednesday(duplication(newTimetable.getWednesday(), oldTimetable.getWednesday()))
            .thursday(duplication(newTimetable.getThursday(), oldTimetable.getThursday()))
            .friday(duplication(newTimetable.getFriday(), oldTimetable.getFriday()))
            .saturday(duplication(newTimetable.getSaturday(), oldTimetable.getSaturday()))
            .sunday(duplication(newTimetable.getSunday(), oldTimetable.getSunday()))
            .build();
    aasClass.setTimetable(JSONUtil.toJsonStr(build));
    return baseMapper.updateById(aasClass);
  }

  /**
   * 利用跨越循环进行课程新旧替换
   *
   * <p>合并新旧列表，根据日期进行升序，然后根据新在前旧在后的固定顺序，进行 prev <-> current <-> next
   * 三者数据的开始时间与接数时间进行比较，保证相同日期中，保留新数据，去除旧数据
   *
   * @param newTimeTable 新课表
   * @param oldTimeTable 旧课表
   * @return {@link List} 课表
   */
  private List<TimeTableCoursesDTO> duplication(
      List<TimeTableCoursesDTO> newTimeTable, List<TimeTableCoursesDTO> oldTimeTable) {
    if (CollectionUtil.isEmpty(oldTimeTable)) {
      return newTimeTable;
    }
    List<TimeTableCoursesDTO> mergeTable =
        Stream.concat(newTimeTable.stream(), oldTimeTable.stream())
            .sorted(
                Comparator.comparing(ttcd -> LocalDateTimeUtil.parse(ttcd.getStartTime(), "HH:mm")))
            .collect(Collectors.toList());
    Iterator<TimeTableCoursesDTO> iterator = mergeTable.iterator();
    TimeTableCoursesDTO prev = null, next;
    while (iterator.hasNext()) {
      TimeTableCoursesDTO current = iterator.next();
      if (prev != null
          && prev.getStartTime().equals(current.getStartTime())
          && prev.getEndTime().equals(current.getEndTime())) {
        iterator.remove();
        prev = null;
        continue;
      }
      if (iterator.hasNext()) {
        next = iterator.next();
        if (current.getStartTime().equals(next.getStartTime())
            && current.getEndTime().equals(next.getEndTime())) {
          // 排期相同
          iterator.remove();
        }
        if (prev == null) {
          prev = next;
        }
      }
    }
    return mergeTable;
  }
}
